PHP OOP: Light and fast Content Management System
December 11, 2020
<?php
$title = 'Blog';
require_once '../class/Post.php';
require_once '../class/PDO/BlogPDO.php';
$images_folder = '../assets/img/uploads/blog/featured_image/';
$pdo = new BlogPDO('../data/blog.db');
$error = null;
$message = null;
// Fletch categories
try {
$query = $pdo->query("SELECT name FROM categories ORDER BY name");
$categories = $query->fetchAll();
} catch (PDOException $e) {
$error = 'PDO error: ' . $e->getMessage();
}
// Posts sorting based on category selection
try {
if(isset($_GET['cat']) && strtolower($_GET['cat']) !== 'all') {
$cat = strtolower($_GET['cat']);
$query = $pdo->query("SELECT id, title, content, date, category, featured_image FROM posts WHERE category LIKE '" . $cat . "' ORDER BY id DESC");
$query2 = $pdo->query("SELECT description FROM categories WHERE name LIKE '" . $cat . "'");
$posts = $query->fetchAll(PDO::FETCH_CLASS, 'Post');
$this_category = $query2->fetch();
if(empty($posts)) {
$message = "No post yet in this category.";
}
} else {
$query = $pdo->query("SELECT id, title, content, date, category, featured_image FROM posts ORDER BY id DESC");
$posts = $query->fetchAll(PDO::FETCH_CLASS, 'Post');
}
} catch (PDOException $e) {
$error = 'PDO error: ' . $e->getMessage();
}
?>
<?php require '../includes/header.php' ?>
<?php if(isset($error)): ?>
<div class="alert alert-danger mb-3"><?= $error ?></div>
<?php else: ?>
<div class="d-flex justify-content-center my-5">
<a href="index.php?cat=all">
<button type="button" class="btn btn<?= isset($_GET['cat']) && $_GET['cat'] !== "all" ? '-outline' : '' ?>-primary btn-sm mx-1">All categories</button>
</a>
<?php foreach($categories as $category): ?>
<a href="index.php?cat=<?= Post::cat_name_format($category->name) ?>">
<button type="button" class="btn btn<?= !isset($_GET['cat']) || (isset($_GET['cat']) && $_GET['cat'] !== strtolower($category->name)) ? '-outline' : '' ?>-primary btn-sm mx-1"><?= ucfirst($category->name) ?></button>
</a>
<?php endforeach ?>
</div>
<?= !empty($this_category->description) ? '<p class="lead text-center mb-5">' . $this_category->description . '</p>' : '' ?>
<?php if(!empty($message)): ?>
<p class="text-center py-5 my-5"><?= $message ?></p>
<?php else: ?>
<div class="row">
<?php foreach($posts as $post): ?>
<div class="col-md-6 mb-4">
<div class="card">
<?php
$has_featured_image = false;
if(!empty($post->featured_image)):
$has_featured_image = true; ?>
<a href="post.php?id=<?= $post->id ?>">
<img class="card-img-top" style="max-height: 200px; object-fit: cover;" src="<?= $images_folder . $post->featured_image ?>" alt="<?= htmlentities($post->title) ?>">
</a>
<?php endif ?>
<div class="card-body">
<a href="post.php?id=<?= $post->id ?>">
<h2><?= htmlentities($post->title) ?></h2>
</a>
<div class="mb-4">
<span class="small text-muted">
<?= $post->getDate() ?>
</span>
<a href="./index.php?cat=<?= Post::cat_name_format($post->category) ?>">
<span class="badge bg-light text-dark pb-1 ml-2"><?= ucfirst(htmlentities($post->category)) ?></span>
</a>
</div>
<?= nl2br(htmlentities($post->getExerpt($has_featured_image))) ?>
<div>
<a href="post.php?id=<?= $post->id ?>">
<button class="btn btn-primary btn-sm mt-4">Read more</button>
</a>
</div>
</div>
</div>
</div>
<?php endforeach ?>
</div>
<?php endif ?>
<?php endif ?>
<?php require '../includes/footer.php'; ?>
<?php
$rootPath = '../';
require_once $rootPath . 'class/Post.php';
require_once $rootPath . 'class/PDO/BlogPDO.php';
require_once $rootPath . 'class/Blog/BreadcrumbBlog.php';
$pictures_folder_root = $rootPath . 'assets/img/';
$featured_images_folder = $pictures_folder_root . 'uploads/blog/featured_image/';
$author_picture_folder = $pictures_folder_root . 'uploads/blog/authors/';
$pdo = new BlogPDO($rootPath . 'data/blog.db');
$error = null;
$isset_author = false;
try {
$query = $pdo->prepare("SELECT * FROM posts WHERE id=:id");
$query->execute([
'id' => $_GET['id']
]);
$query->setFetchMode(PDO::FETCH_CLASS, 'Post');
$post = $query->fetch();
if(isset($post->author)) { // retrieve author
$query = $pdo->query("SELECT name FROM authors WHERE name LIKE '" . $post->author . "'");
$author = $query->fetch();
if (!empty($author)) {
$isset_author = true;
$query = $pdo->query("SELECT * FROM authors WHERE name = '" . $author->name . "'");
$author = $query->fetch();
}
}
} catch (PDOException $e) {
$error = 'PDO error: ' . $e->getMessage();
}
empty($error) ? $title = htmlentities($post->title) : $title = "Post";
$bc_post = new BreadcrumbBlog ("Blog", [ ucfirst($post->category) => '?cat=' . Post::cat_name_format($post->category) ], $title);
?>
<?php require $rootPath . 'includes/header.php' ?>
<?php if(!empty($error)): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php else: ?>
<div class="pb-3"> <!-- post header -->
<?php if(!empty($post->featured_image)): ?>
<div class="card text-white border-0">
<img class="card-img" style="max-height:18rem; object-fit: cover;" src="<?= $featured_images_folder . $post->featured_image ?>" alt="<?= $title ?>" />
<div class="card-img-overlay d-flex flex-column justify-content-center" style="background-color: #00000060">
<div>
</div>
<div>
<h1><?= htmlentities($post->title) ?></h1>
<div class="small text-capitalize"><?= isset($post->date)? $post->getDate('long') : '' ?> - By <?= isset($post->author)? ucwords(htmlentities($post->author)) : '' ?></div>
<?php if(isset($post->category)): ?>
<a href="./index.php?cat=<?= Post::cat_name_format($post->category) ?>">
<div class="badge bg-light text-dark pb-1 mt-3">
<?= Post::cat_name_format($post->category) ?>
</div>
</a>
<?php endif ?>
</div>
</div>
</div>
<?php else: ?>
<h1><?= $title ?></h1>
<div class="small"><?= isset($post->date)? $post->getDate('long') : '' ?> - By <?= isset($post->author)? $post->author : '' ?></div>
<hr>
<?php endif; ?>
<!-- breadcrumb -->
<div class="small text-muted my-2"><?= $bc_post->show_breadcrumb() ?></div>
<hr>
<div class="mt-4"> <!-- post content -->
<p class="lead mt-5"><?= isset($post->introduction)? htmlentities($post->introduction) : '' ?></p>
<div class="my-5">
<?= isset($post->content)? nl2br(htmlentities($post->content)) : '' ?>
</div>
</div>
<div> <!-- post footer-->
<?php if ($isset_author): ?>
<hr>
<div class="card bg-light mt-5" style="max-width: 30rem;"> <!-- author box -->
<div class="card-header small text-muted text-capitalize">About <?= ucwords(htmlentities($author->name)) ?></div>
<div class="media card-body align-items-center">
<img class="mr-3" style="border-radius:100%; width: 4rem; height:auto" src="<?= !empty($author->picture)? $author_picture_folder . $author->picture : $pictures_folder_root . 'avatar-default.png' ?>" />
<div class="media-body">
<?= !empty($author->bio)? htmlentities($author->bio) : 'I am a contributor to the blog. From time to time, i share my expertise with you through an article.' ?>
</div>
</div>
</div>
<?php endif ?>
</div>
</div>
<?php endif ?>
<?php require $rootPath . 'includes/footer.php' ?>
<?php
require_once '../includes/functions/login.php';
redirect_if_not_connected('../pages/login.php');
require_once '../includes/functions/admin_dashboard.php';
$title = greetings(credentials()['username']);
require '../includes/header.php';
?>
<p class="lead mb-4">What do you want to do?</p>
<div class="row pt-3 pb-3">
<?php foreach(admin_dashboard_tiles() as $tile): ?>
<div class="col-sm-3" style="align-items: stretch">
<a href="<?= $tile['link'] ?>" class="link-unstyled">
<div class="<?= 'card ' . $tile['color'] . ' mb-3'?>">
<div class="card-header small"><?= $tile['header'] ?></div>
<div class="card-body" style="display:flex; flex-direction:column; justify-content:space-between">
<div>
<h5 class="card-title"><?= $tile['title'] ?></h5>
<p class="card-text small"><?= $tile['text'] ?></p>
</div>
<div class="<?= 'btn ' . $tile['btn-color'] . ' btn-block mt-3'?>"><?= $tile['button'] ?></div>
</div>
</div>
</a>
</div>
<?php endforeach ?>
</div>
<?php require '../includes/footer.php'; ?>
<?php
$rootPath = '../../';
$modals_id = [];
require_once $rootPath . "includes/functions/login.php";
require_once $rootPath . "class/Blog/PostTypeTable.php";
require_once $rootPath . "class/Blog/BreadcrumbBlog.php";
redirect_if_not_connected($rootPath . 'pages/login.php');
$db_tables = [
'posts' => [
'fields' => ['id', 'title'],
'order_by' => 'id DESC',
'singular' => 'post',
'main_field' => 'title',
],
];
$index = new PostTypeTable($db_tables, $rootPath);
$index->getPostTypeTable();
require $rootPath . 'includes/footer.php';
<?php
$rootPath = '../../';
$modals_id = $sections = $tables = [];
require_once $rootPath . "includes/functions/login.php";
require_once $rootPath . "class/Blog/PostTypeTable.php";
require_once $rootPath . "class/Blog/BreadcrumbBlog.php";
redirect_if_not_connected($rootPath . 'pages/login.php');
$db_tables = [
'categories' => [
'fields' => ['id', 'name'],
'order_by' => 'id ASC',
'singular' => 'category',
'main_field' => 'name',
],
'authors' => [
'fields' => ['id', 'name'],
'order_by' => 'id',
'singular' => 'author',
'main_field' => 'name',
]
];
$index = new PostTypeTable($db_tables, $rootPath);
$index->getPostTypeTable(false);
require '../../includes/footer.php';
<?php
$rootPath = '../../';
require_once $rootPath . "includes/functions/login.php";
require_once $rootPath . 'class/PDO/BlogPDO.php';
require_once $rootPath . 'class/Blog/BreadcrumbBlog.php';
redirect_if_not_connected($rootPath . 'pages/login.php');
// Start editing
$pt_singular = 'post';
$pt_plural = 'posts';
$main_field = 'title';
$prepare_vars = 'title, introduction, content, date, author, category';
$has_upload = true;
$picture_field_name = 'featured_image';
if(isset($_POST['title'], $_POST['content'])) {
$execute_command = [
// "picture" and "id" lines are automatically added by the script (so don't add them here)
'title' => $_POST['title'],
'introduction' => $_POST['introduction'],
'content' => $_POST['content'],
'date' => time(),
'author' => $_POST['author'],
'category' => $_POST['category'],
];
}
$index_redirection = 'index.php';
// Stop editing
require './parts/edit-queries.php'; // SQL Queries
require './parts/' . strtolower($pt_singular) . '-form.php'; // Form (Edit & Add new)
require $rootPath . 'includes/footer.php';
<?php
$rootPath = '../../';
require_once $rootPath . "includes/functions/login.php";
require_once $rootPath . 'class/PDO/BlogPDO.php';
require_once $rootPath . 'class/Blog/BreadcrumbBlog.php';
redirect_if_not_connected($rootPath . 'pages/login.php');
// Start editing
$pt_singular = 'category';
$pt_plural = 'categories';
$main_field = 'name';
$prepare_vars = 'name, description';
$has_upload = false;
$picture_field_name = '';
if(isset($_POST['name'])) {
$execute_command = [
// "picture" and "id" lines are automatically added by the script (so don't add them here)
'name' => $_POST['name'],
'description' => $_POST['description']
];
}
$index_redirection = 'index-cat-auth.php';
// Stop editing
require './parts/edit-queries.php'; // SQL Queries
require './parts/' . strtolower($pt_singular) . '-form.php'; // Form (Edit & Add new)
require $rootPath . 'includes/footer.php';
<?php
$rootPath = '../../';
require_once $rootPath . "includes/functions/login.php";
require_once $rootPath . 'class/PDO/BlogPDO.php';
require_once $rootPath . 'class/Blog/BreadcrumbBlog.php';
redirect_if_not_connected($rootPath . 'pages/login.php');
// Start editing
$pt_singular = 'author';
$pt_plural = 'authors';
$main_field = 'name';
$prepare_vars = 'name, bio';
$has_upload = true;
$picture_field_name = 'picture';
if(isset($_POST['name'], $_POST['bio'])) {
$execute_command = [
// "picture" and "id" lines are automatically added by the script (so don't add them here)
'name' => $_POST['name'],
'bio' => $_POST['bio']
];
}
$index_redirection = 'index-cat-auth.php';
// Stop editing
require './parts/edit-queries.php'; // SQL Queries
require './parts/' . strtolower($pt_singular) . '-form.php'; // Form (Edit & Add new)
require $rootPath . 'includes/footer.php';
<?php
$pictures_folder = $rootPath . 'assets/img/uploads/blog/featured_image/';
require_once $rootPath . 'class/FileUpload/ImageUploader.php';
require_once $rootPath . 'class/Modals/Modal.php';
// Fill select lists with authors and categories
$pdo = new BlogPDO('../../data/blog.db');
$error = null;
try {
$query = $pdo->query("SELECT name FROM categories ORDER BY id ASC");
$categories = $query->fetchAll();
$query = $pdo->query("SELECT name FROM authors ORDER BY id ASC");
$authors = $query->fetchAll();
} catch (PDOException $e) {
$error = $e->getMessage();
}
if(isset($_FILES["fileToUpload"])) {
$upload_image = new ImageUploader($_FILES["fileToUpload"], $pictures_folder);
$upload_image->upload_no_check();
}
?>
<form action="" method="post" enctype="multipart/form-data">
<div class="row">
<div class="col col-md-8">
<p class="lead mb-5">Fields with an asterisk are mandatory.</p>
<div class="form-group">
<label>
Post title*
<small class="small text-muted form-text">60 characters max. are recommended</small>
</label>
<input type="text" name="title" class="form-control" required value="<?= $isEdit && isset($postType)? $postType->title : '' ?>">
</div>
<div class="form-group">
<label>
Introduction
<div class="small text-muted">This text will be displayed in bigger letters than the content</div>
</label>
<textarea rows="3" class="form-control" name="introduction"><?= $isEdit && isset($postType->introduction) ? $postType->introduction : '' ?></textarea>
</div>
<div class="form-group">
<label>
Content*
<div class="small text-muted">HTML not allowed for security purpose</div>
</label>
<textarea rows="15" class="form-control" name="content" required><?= $isEdit && isset($postType)? $postType->content : '' ?></textarea>
</div>
</div>
<div class="col col-md-4">
<div class="sticky-top pt-4">
<button type="submit" class="btn btn-primary btn-block mb-3">
<svg width="1.2em" height="1.2em" viewBox="0 0 16 16" class="bi bi-check2" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/></svg>
Publish
</button>
<div class="d-flex mb-5">
<?php if(empty($_GET['p']) || $_GET['p'] !== 'add'): ?>
<a class="flex-grow-1 mr-3" href="<?= BreadcrumbBlog::PROJECT_ROOT . 'blog/' . $pt_singular . '.php?id=' . $postType->id ?>">
<button type="button" class="btn btn-outline-primary w-100">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-eye" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.134 13.134 0 0 0 1.66 2.043C4.12 11.332 5.88 12.5 8 12.5c2.12 0 3.879-1.168 5.168-2.457A13.134 13.134 0 0 0 14.828 8a13.133 13.133 0 0 0-1.66-2.043C11.879 4.668 10.119 3.5 8 3.5c-2.12 0-3.879 1.168-5.168 2.457A13.133 13.133 0 0 0 1.172 8z"/><path fill-rule="evenodd" d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/></svg>
View
</button>
</a>
<?php $modal_delete = new Modal ($pt_singular . '-del-confirm'); ?>
<?php $modal_delete->showModalTrigger($postType->id,
'<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-trash" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg>
Delete', 'button', 'btn btn-outline-danger flex-grow-1') ?>
<?php endif ?>
</div>
<div class="form-group">
<label>Category</label>
<select name="category" class="form-control">
<?php foreach($categories as $category): ?>
<option value="<?= $category->name ?>" <?= $isEdit && isset($postType->category) && $category->name === $postType->category ? 'selected' : '' ?> >
<?= ucwords($category->name) ?>
</option>
<?php endforeach ?>
</select>
</div>
<div class="form-group">
<label>Featured image</label>
<input type="file" name="fileToUpload" id="fileToUpload" accept="image/*">
<?php if ($isEdit && isset($postType) && !empty($postType->featured_image)): ?>
<div class="mt-2" style="position: relative">
<div style="color: white; padding: 5px 10px; background-color: #00000080; position: absolute; right: 0">
<label style="margin:0">
<input type="checkbox" name="file-delete" class="mr-1">
Remove file
</label>
</div>
</div>
<img src="<?= $pictures_folder . $postType->featured_image ?>" style="width:100%" />
<?php endif ?>
</div>
<div class="form-group">
<label>Author</label>
<select name="author" class="form-control">
<?php foreach($authors as $author): ?>
<option value="<?= $author->name ?>" <?= $isEdit && isset($postType->author) && $postType->author === $author->name ? 'selected' : '' ?> >
<?= ucwords($author->name) ?>
</option>
<?php endforeach ?>
</select>
</div>
</div>
</div>
</div>
</form>
<?php if(empty($_GET['p']) || $_GET['p'] !== 'add'): ?>
<?php $modal_delete->showModal($postType->id, './delete.php?type=' . $pt_singular . '&id=' . $postType->id); ?>
<?php endif;
<?php
$pictures_folder = $rootPath . 'assets/img/uploads/blog/featured_image/';
require_once $rootPath . 'class/Modals/Modal.php';
?>
<form action="" method="post">
<div class="row">
<div class="col col-md-8">
<p class="lead mb-5">Fields with an asterisk are mandatory.</p>
<div class="form-group">
<label>
Category name*
<small class="small text-muted form-text">1 or 2 words max. are recommended</small>
</label>
<input type="text" name="name" class="form-control" required value="<?= isset($postType)? $postType->name : '' ?>">
</div>
<div class="form-group">
<label>
Description
<div class="small text-muted">A short text that describes the category's content.</div>
</label>
<textarea rows="3" class="form-control" name="description"><?= isset($postType->description) ? $postType->description : '' ?></textarea>
</div>
</div>
<div class="col col-md-4">
<div class="sticky-top pt-4">
<div class="d-flex mb-5">
<button type="submit" class="btn btn-primary flex-grow-1">
<svg width="1.2em" height="1.2em" viewBox="0 0 16 16" class="bi bi-check2" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/></svg>
Publish
</button>
<?php if(empty($_GET['p']) || $_GET['p'] !== 'add'): ?>
<?php $modal_delete = new Modal ($pt_singular . '-del-confirm'); ?>
<?php $modal_delete->showModalTrigger($postType->id,
'<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-trash" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg>',
'button', 'btn btn-outline-danger ml-3') ?>
<?php endif ?>
</div>
</div>
</div>
</div>
</form>
<?php if(empty($_GET['p']) || $_GET['p'] !== 'add'): ?>
<?php $modal_delete->showModal($postType->id, './delete.php?type=' . $pt_singular . '&id=' . $postType->id); ?>
<?php endif; ?>
<?php
$pictures_folder = $rootPath . 'assets/img/uploads/blog/authors/';
require_once $rootPath . 'class/FileUpload/ImageUploader.php';
require_once $rootPath . 'class/Modals/Modal.php';
if(isset($_FILES["fileToUpload"])) {
$upload_image = new ImageUploader($_FILES["fileToUpload"], $pictures_folder);
$upload_image->upload_no_check();
}
?>
<form action="" method="post" enctype="multipart/form-data">
<div class="row">
<div class="col col-md-8">
<p class="lead mb-5">Fields with an asterisk are mandatory.</p>
<div class="form-group">
<label>
Name*
<small class="small text-muted form-text">30 characters max. recommended</small>
</label>
<input type="text" name="name" class="form-control" required value="<?= isset($postType)? $postType->name : '' ?>">
</div>
<div class="form-group">
<label>
Bio
<div class="small text-muted">Describe yourself in a few words.</div>
</label>
<textarea rows="3" class="form-control" name="bio"><?= isset($postType->bio) ? $postType->bio : '' ?></textarea>
</div>
</div>
<div class="col col-md-4">
<div class="sticky-top pt-4">
<div class="d-flex mb-5">
<button type="submit" class="btn btn-primary flex-grow-1">
<svg width="1.2em" height="1.2em" viewBox="0 0 16 16" class="bi bi-check2" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/></svg>
Publish
</button>
<?php if(empty($_GET['p']) || $_GET['p'] !== 'add'): ?>
<?php $modal_delete = new Modal ($pt_singular . '-del-confirm'); ?>
<?php $modal_delete->showModalTrigger($postType->id,
'<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-trash" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg>',
'button', 'btn btn-outline-danger ml-3') ?>
<?php endif ?>
</div>
<div class="form-group">
<label>Picture / Avatar</label><br>
<input type="file" name="fileToUpload" id="fileToUpload" accept="image/*">
<?php if (isset($postType) && !empty($postType->picture)): ?>
<div class="mt-2" style="position: relative">
<div style="color: white; padding: 5px 10px; background-color: #00000080; position: absolute; right: 0">
<label style="margin:0">
<input type="checkbox" name="file-delete" class="mr-1">
Remove file
</label>
</div>
</div>
<img src="<?= $pictures_folder . $postType->picture ?>" style="width:100%" />
<?php endif ?>
</div>
</div>
</div>
</div>
</form>
<?php if(empty($_GET['p']) || $_GET['p'] !== 'add'): ?>
<?php $modal_delete->showModal($postType->id, './delete.php?type=' . $pt_singular . '&id=' . $postType->id); ?>
<?php endif; ?>
<?php
$isEdit = true;
if(!empty($_GET['p']) && $_GET['p'] === "add") {
$isEdit = false;
}
$pdo = new BlogPDO($rootPath . 'data/blog.db');
$error = null;
$success = null;
try {
// ADD NEW
if(!$isEdit) {
if (!empty($_POST[$main_field])) {
if ($has_upload) {
$prepare_vars .= ', ' . $picture_field_name;
}
$prepare_vars_ids = ":" . str_replace(',', ', :', str_replace(' ', '', $prepare_vars));
isset($_FILES['fileToUpload']['name']) ? $picture_name = $_FILES['fileToUpload']['name'] : $picture_name = null;
$query = $pdo->prepare("INSERT INTO " . $pt_plural . " (" . $prepare_vars . ") VALUES (" . $prepare_vars_ids . ")");
if ($has_upload) { // Add 1 variable
$execute_command[$picture_field_name] = $picture_name;
}
$query->execute($execute_command);
header("location:./edit-" . strtolower($pt_singular) . ".php?id=" . $pdo->lastInsertId() . "&i=new");
}
// EDIT
} else {
// Fill "value" fields attribute for this post
$query = $pdo->prepare("SELECT * FROM " . $pt_plural . " WHERE id = :id");
$query->execute([ 'id' => $_GET['id'] ]);
$postType = $query->fetch(); // $postType is used to prefill the form inside edit-*.php
// Update data
if (!empty($_POST[$main_field])) {
// File upload conditions
if ($has_upload) {
$picture_name = $_FILES['fileToUpload']['name'];
// To not replace $picture_name by '' while updating post without choising a new image:
if ($picture_name === '' && !isset($_POST['file-delete'])) {
$query = $pdo->prepare("SELECT " . $picture_field_name . " FROM " . $pt_plural . " WHERE id = :id");
$query->execute([ 'id' => $_GET['id'] ]);
$picture = $query->fetch();
$picture_name = $picture->$picture_field_name;
} elseif (isset($_POST['file-delete']) && $_POST['file-delete'] === 'on') { // If "Remove file" is checked
$picture_name = null;
}
}
// Update query
// Generate vars list ($ptResult) from $prepare_vars
if ($has_upload) { // Add 1 variable
$prepare_vars .= ", " . $picture_field_name;
}
$ptInput = (array)explode(',', str_replace(' ', '', $prepare_vars));
foreach($ptInput as $ptItem) {
$ptItem = $ptItem . ' = :' . $ptItem; $ptResult[] = $ptItem;
}
$ptResult = implode(', ', $ptResult);
$query = $pdo->prepare("UPDATE " . $pt_plural . " SET " . $ptResult . " WHERE id = :id");
if ($has_upload) { // Add 2 variables
$execute_command[$picture_field_name] = $picture_name;
}
$execute_command['id'] = $_GET['id'];
$query->execute($execute_command);
// Redirect
header("location:./edit-" . strtolower($pt_singular) . ".php?id=" . $_GET['id'] . "&i=edit");
}
}
} catch (PDOException $e) {
$error = ucfirst($pt_singular) . ' was not saved because of an error:<br><small>' . $e->getMessage() . '</small>';
}
if (!empty($_GET['i'])) {
$success = ucfirst($pt_singular) . ' published successfully. Go back to <a href="' . $rootPath . 'admin/blog/' . $index_redirection . '">list</a>';
}
$isEdit ? $title = "Edit " . strtolower($pt_singular) : $title = "Add a new " . strtolower($pt_singular);
// Breadcrumb
$bc_post = new BreadcrumbBlog ("Admin", [ ucfirst($pt_plural) => 'blog/' . $index_redirection ], $title);
require $rootPath . 'includes/header.php';
?>
<!-- breadcrumb -->
<div class="small text-muted my-2"><?= $bc_post->show_breadcrumb() ?></div>
<hr>
<?php
if(!empty($error)): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php elseif(isset($success)): ?>
<div class="alert alert-success"><?= $success ?></div>
<?php endif;
<?php
class BlogPDO extends PDO {
public function __construct($dns, $username = null, $password = null, $type = 'sqlite') {
parent::__construct($type . ':' . $dns, $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ
]);
}
public function pluralize(string $word)
{
switch($word) {
case 'post': return 'posts'; break;
case 'category': return 'categories'; break;
case 'author': return 'authors'; break;
default: return '';
}
}
}
<?php
$root = dirname(__DIR__) . DIRECTORY_SEPARATOR;
require_once $root . 'PDO' . DIRECTORY_SEPARATOR . 'BlogPDO.php';
require_once $root . 'Modals' . DIRECTORY_SEPARATOR . 'Modal.php';
class PostTypeTable {
public function __construct(array $db_tables, string $rootPath)
{
$this->db_tables = $db_tables;
$this->rootPath = $rootPath;
}
public function getPostTypeTable(bool $show_view_btn = true)
{
$error = null;
$pdo = new BlogPDO($this->rootPath . 'data/blog.db');
foreach($this->db_tables as $table_name => $data) {
$fields = implode(', ', $data['fields']);
try {
$query = $pdo->query("SELECT " . $fields . " FROM " . $table_name . " ORDER BY " . $data['order_by']);
$tables[$table_name] = $query->fetchAll();
if(isset($_GET['id']) && isset($_GET['info'])) {
if($_GET['info'] === 'delete-cat') {
$query = $pdo->prepare("DELETE FROM " . $table_name . " WHERE id = :id");
var_dump($query);
$query->execute([
'id' => $_GET['id']
]);
}
}
} catch (PDOException $e) {
$errors[] = $data['singular'] . ' not deleted because of a query error:<br><small>' . $e->getMessage() . '<br><pre>' . var_dump($query) . '</pre></small>';
}
if(isset($_GET['info'])) {
if($_GET['info'] === 'del-success') {
$success = ucfirst($_GET['type']) . " deleted successfully";
} elseif($_GET['info'] === 'del-noid') {
$error = ucfirst($_GET['type']) . " was not deleted because no post ID were submited. Please delete posts from <a href='./index.php'>here</a>";
} elseif($_GET['info'] === 'del-error') {
$error = ucfirst($_GET['type']) . " not deleted because of a SQL error: " . $_GET['msg'];
}
}
$sections[] = $table_name;
}
if(count($sections) > 1) {
$title_str = ucwords(implode(', ', $sections));
$title = substr_replace($title_str, ' & ', strrpos($title_str, ', '), 2);
} else {
$title = ucwords($sections[0]);
}
// Breadcrumb
$bc_post = new BreadcrumbBlog ("Admin", [], $title);
require $this->rootPath . 'includes/header.php'; ?>
<!-- breadcrumb -->
<div class="small text-muted my-2"><?= $bc_post->show_breadcrumb() ?></div>
<hr>
<? if(!empty($error)) {
echo '<div class="alert alert-danger mb-3">' . $error . '</div>';
} elseif(!empty($success)) {
echo '<div class="alert alert-success mb-3">' . $success . '</div>';
}
$col_width = 12;
if(count($this->db_tables) > 1) {
$col_width = 6;
}
?>
<p class="lead mb-5">Choose an item to edit.</p>
<div class="row mt-5">
<?php foreach ($tables as $table_name => $table_items):
$table_name_singular = $this->db_tables[$table_name]['singular'];
$main_field = (string)$data['main_field'] ?>
<div class="col-md-<?= $col_width ?> mb-4">
<div class="d-flex align-items-center mb-3">
<h2><?= ucfirst($table_name) ?></h2>
<a href="<?= $this->rootPath . 'admin/blog/edit-' . $table_name_singular . '.php?p=add' ?>">
<button class="btn btn-primary ml-3">Add new</button>
</a>
</div>
<div class="card">
<table class="table">
<thead class="thead-light">
<tr>
<th>Post</th>
<th>Options</th>
<th>ID</th>
</tr>
</thead>
<tbody>
<?php foreach($table_items as $item):
$modal_delete[$table_name] = new Modal ($table_name . '-del-confirm');
$modals_id[$table_name][] = $item->id ?>
<tr>
<td style="vertical-align: middle"><?= htmlentities($item->$main_field) ?></td>
<td style="vertical-align: middle">
<div class="my-1">
<a href="<?= $this->rootPath . 'admin/blog/edit-' . $table_name_singular . '.php?id=' . $item->id ?>">
<button class="btn btn-primary btn-sm mr-2">Edit</button>
</a>
<?php if($show_view_btn): ?>
<a href="<?= $this->rootPath . 'blog/' . $table_name_singular . '.php?id=' . $item->id ?>">
<button class="btn btn-success btn-sm mr-2">View</button>
</a>
<?php endif ?>
<?php $modal_delete[$table_name]->showModalTrigger($item->id, 'Delete', 'button', 'btn-danger btn-sm') ?>
</div>
</td>
<td class="small text-muted" style="vertical-align: middle">#<?= $item->id ?></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
</div>
</div>
<?php endforeach ?>
</div>
<?php
foreach($this->db_tables as $table_name => $data) {
foreach($modals_id[$table_name] as $modal_id) {
$modal_delete[$table_name]->showModal($modal_id, './delete.php?type=' . $data['singular'] . '&id=' . $modal_id);
}
}
}
}
<?php
class Post {
public function getDate(string $length = '')
{
$date = new DateTime("@" . $this->date);
switch($length) {
case 'long': $f = "F j, Y"; break;
case 'short': $f = "m/d/Y"; break;
default: $f = "M j, Y";
}
return $date->format("M j, Y");
}
public function getExerpt(bool $has_featured_image = true)
{
$has_featured_image ? $max_letters = 200 : $max_letters = 500;
if(strlen($this->content) > $max_letters) {
$exerpt = wordwrap($this->content, $max_letters, '__break__');
$array = explode('__break__', $exerpt);
return $array[0] . ' ...';
} else {
return $this->content;
}
}
public static function cat_name_format(string $category_name)
{
return strtolower(str_replace(['-','_',' '], '-', $category_name));
}
}
<?php
class BreadcrumbBlog {
public const PROJECT_ROOT = DIRECTORY_SEPARATOR . 'projects' . DIRECTORY_SEPARATOR . 'php-playground' . DIRECTORY_SEPARATOR;
private const LAST_PART_LENGTH = 30; // Letters
private const DELIMITER = '>';
public function __construct(string $root_step_title, array $middle_steps, string $current_page_title) {
$this->root_step_title = $root_step_title;
$this->middle_steps = $middle_steps;
$this->current_page_title = $current_page_title;
}
public function show_breadcrumb()
{
$root_folder = self::PROJECT_ROOT . strtolower($this->root_step_title);
$root_step = '<a href="' . $root_folder . '">' . $this->root_step_title . '</a> ' . self::DELIMITER . ' ';
$middle_steps = '';
foreach ($this->middle_steps as $bc_title => $bc_link) {
$middle_steps .= '<a href="' . $root_folder . DIRECTORY_SEPARATOR . $bc_link . '">' . $bc_title . '</a> ' . self::DELIMITER . ' ';
}
$last_step = $this->get_bc_exerpt();
return $root_step . $middle_steps . $last_step ;
}
private function get_bc_exerpt()
{
if(strlen($this->current_page_title) > 30) {
$exerpt = wordwrap($this->current_page_title, self::LAST_PART_LENGTH, '__break__');
$array = explode('__break__', $exerpt);
return ucfirst($array[0]) . ' ...';
} else {
return $this->current_page_title;
}
}
}
<?php
class Modal {
function __construct(string $id)
{
$this->modal_group_name = $id;
}
public function showModalTrigger(string $post_type_id, string $title, string $link_type = "button", string $class = "btn-primary"): void
{
if ($link_type === "button") { ?>
<button type="button" class="btn <?= $class ?>" data-toggle="modal" data-target="#<?= $this->modal_group_name . '-' . $post_type_id ?>">
<?= $title ?>
</button><?php
} else { ?>
<a href="#" class="<?= $class ?>">
<?= $title ?>
</a><?php
}
}
public function showModal(string $post_type_id, string $confirm_link, string $title = 'Are you sure?', string $content = 'The content will be deleted permanently, with no way to recover.'): void
{
?>
<div class="modal fade" id="<?= $this->modal_group_name . '-' . $post_type_id ?>" tabindex="-1" role="dialog" >
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><?= $title ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<?= $content ?>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<a href="<?= $confirm_link ?>"><button type="button" class="btn btn-primary">Confirm</button></a>
</div>
</div>
</div>
</div>
<?php
}
}